/**
 * 
 */
package commons.base.netty.http;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.alibaba.fastjson.JSON;

import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.CharsetUtil;

/**
 * 处理HTTP请求的任务
 * @author yuan<cihang.yuan@happyelements.com>
 * @since 1.7
 */
public class HttpTask implements Runnable, NettyHttpResponse {

	private static final Logger log = LogManager.getLogger(HttpTask.class);
	
	/** URI路由器 */
	private HttpRouter httpRouter;
	private ChannelHandlerContext ctx;
	private FullHttpRequest request;
	private ChannelInvalidPolicy channelInvalidPolicy;
	
	public HttpTask(HttpRouter httpRouter, ChannelHandlerContext ctx, FullHttpRequest request, ChannelInvalidPolicy channelInvalidPolicy) {
		super();
		this.httpRouter = httpRouter;
		this.ctx = ctx;
		this.request = request;
	}

	@Override
	public void run() {
		if((channelInvalidPolicy == ChannelInvalidPolicy.DISCARDS) && !ctx.channel().isActive()){
			// 可能连接已经超时被关闭了
			log.error("channel is closed. " + ctx.channel() + ". " + request.uri());
			request.release();
			return ;
		}
		
		try{
	        NettyHttpServlet servlet = httpRouter.getServlet(getPath(request));
	        
	        if(servlet == null){
	        	write404(ctx);
	        }else{
	        	servlet.processRequest(request, this);
	        }
		} catch (Throwable e) {
			log.error("[HttpTask]" + e.getMessage(), e);
		}finally {
			request.release();
			ctx.channel().flush();
			ctx.channel().close();
		}
	}
	
	private void write404(ChannelHandlerContext ctx)throws Exception{
		sendHttpResponse(ctx, HttpResponseStatus.NOT_FOUND, "text/plain; charset=UTF-8", "");
	}
	
	private void write500(ChannelHandlerContext ctx, String result)throws Exception{
		sendHttpResponse(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR, "text/plain; charset=UTF-8", result);
	}
	
//	private void writeOK(ChannelHandlerContext ctx, String result)throws Exception{
//		sendHttpResponse(ctx, HttpResponseStatus.OK, "text/plain; charset=UTF-8", result);
//	}
	
	private String getPath(FullHttpRequest req){
		String uri = req.uri();
		
		int index = uri.indexOf('?');
		if(index != -1){
			uri = uri.substring(0, index);
		}
		
		return uri;
	}
	
	private void sendHttpResponse(ChannelHandlerContext ctx, HttpResponseStatus status, String contentType, String result)
			throws Exception {
		FullHttpResponse res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status);
		
        res.headers().set(HttpHeaderNames.CONTENT_TYPE, contentType);
        res.headers().set(HttpHeaderNames.CONTENT_LENGTH, res.content().readableBytes());
        if(result != null){
        	res.content().writeBytes(result.getBytes(CharsetUtil.UTF_8));
        }
        
		// Send the response and close the connection if necessary.
		ChannelFuture f = ctx.channel().writeAndFlush(res);
		if (!HttpUtil.isKeepAlive(request) || res.status().code() != 200) {
			f.addListener(ChannelFutureListener.CLOSE);
		}
	}
	
	@Override
	public void renderJson(Object result) {
		// 返回响应
		String jsonText = "";
		if(result != null){
			if(result instanceof String){
				jsonText = (String)result;
			}else{
				jsonText = JSON.toJSONString(result);
			}
		}
		
		try {
			sendHttpResponse(ctx, HttpResponseStatus.OK, "text/plain; charset=UTF-8", jsonText);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
	}

	@Override
	public void renderHtml(String result) {
		try {
			sendHttpResponse(ctx, HttpResponseStatus.OK, "text/html; charset=UTF-8", result);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
	}

	@Override
	public void closeChannel() {
		ctx.channel().close();
	}

	@Override
	public void render500(String result) {
		try {
			write500(ctx, result);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
	}

}
